package com.boxman;

import android.content.res.AssetManager;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.os.Handler;
import android.os.Handler.Callback;
import android.os.Message;
import android.view.View;

import java.io.InputStream;
import java.util.LinkedList;
import java.util.Locale;

// 游戏
class Game {
    // 移动结点
    static class MoveNode {
        private final BaseGameObject box;   // 被推动的箱子
        private final int direction;        // 移动方向

        /**
         * 构造方法。
         * @param box       被推动的箱子
         * @param direction 移动方向
         */
        public MoveNode(BaseGameObject box, int direction) {
            this.box = box;
            this.direction = direction;
        }

        /**
         * 获取箱子。
         * @return 箱子
         */
        BaseGameObject getBox() {
            return box;
        }

        /**
         * 获取方向。
         * @return 方向
         */
        int getDirection() {
            return direction;
        }
    }

    // 状态
    public enum Status {
        END,    // 已结束
        PLAYING // 游戏中
    }

    private final AssetManager mAssetManager;           // 资产管理者
    private int mBoxCount;                              // 未到达目的地的箱子数
    private final Handler mCallback;                    // 回调
    private final Canvas mDrawer;                       // 绘图者
    private final View mGameBoard;                      // 游戏区域
    private int mLevel;                                 // 关卡数
    private Man mMan;                                   // 小人
    private int mMoveCount;                             // 移动次数
    private final LinkedList<MoveNode> mMoveList;       // 所有移动结点
    public final Paint mPaint;                          // 绘图信息
    private Status mStatus;                             // 状态
    public static BaseGameObject[][] sBaseGameObjects;  // 所有游戏物体

    /**
     * 构造方法。
     * @param callback  回调
     * @param drawer    绘图者
     * @param gameBoard 游戏区域
     */
    public Game(Callback callback, Canvas drawer, View gameBoard) {
        // 初始化成员
        mBoxCount = 0;
        mCallback = new Handler(callback);
        mDrawer = drawer;
        mGameBoard = gameBoard;
        mLevel = 1;
        mMoveCount = 0;
        mMoveList = new LinkedList<>();
        mPaint = new Paint();
        mPaint.setStyle(Style.FILL);
        mPaint.setColor(Const.GAME_BOARD_COLOR);
        mAssetManager = gameBoard.getResources().getAssets();
        mStatus = Status.END;

        // 进入第1关
        this.enterLevel();
    }

    /**
     * 进入关卡。
     */
    private void enterLevel() {
        // 小人停止跳舞
        if (mMan != null) {
            mMan.stopDancing();
        }

        // 重绘游戏区域
        mDrawer.drawRect(0, 0, mGameBoard.getWidth(), mGameBoard.getHeight(), mPaint);

        // 删除所有物体
        sBaseGameObjects = new BaseGameObject[Const.GAME_BOARD_GRID_COUNT_V][Const.GAME_BOARD_GRID_COUNT_H];

        // 删除所有移动结点
        mMoveList.clear();

        // 读取关卡文件
        final String levelFilePath = String.format(Locale.getDefault(), Const.LEVEL_FILE_PATH, mLevel);
        InputStream reader;
        try {
            reader = mAssetManager.open(levelFilePath);
        } catch (Exception e) {
            return;
        }

        // 创建所有物体
        int read = -1;
        BaseGameObject.Type type;
        mBoxCount = 0;
        for (int y = 0; y < Const.GAME_BOARD_GRID_COUNT_V; y++) {
            for (int x = 0; x < Const.GAME_BOARD_GRID_COUNT_H; x++) {
                try {
                    read = reader.read();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                if (read == '0') {
                    continue;
                }
                if (read == '\r' || read == '\n') {
                    x--;
                    continue;
                }

                type = BaseGameObject.Type.values()[(read - '0')];

                // 创建小人
                if (type == BaseGameObject.Type.MAN) {
                    sBaseGameObjects[y][x] = mMan = new Man(mAssetManager,
                        new BaseGameObject(mAssetManager, mDrawer, BaseGameObject.Type.BACKGROUND, x, y), mDrawer, mGameBoard,type, x, y);
                    continue;
                }

                // 创建箱子
                if (type == BaseGameObject.Type.BOX) {
                    sBaseGameObjects[y][x] = new Box(mAssetManager,
                        new BaseGameObject(mAssetManager, mDrawer, BaseGameObject.Type.BACKGROUND, x, y), mDrawer, type, x, y);
                    mBoxCount++;
                    continue;
                }

                // 创建背景、目的地、墙
                sBaseGameObjects[y][x] = new BaseGameObject(mAssetManager, mDrawer, type, x, y);
            }
        }

        // 关闭文件
        try {
            reader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 重置成员变量
        mMoveCount = 0;
        mStatus = Status.PLAYING;

        // 刷新游戏区域
        mGameBoard.invalidate();

        // 发送通知
        final Message message = Message.obtain();
        message.what = Const.UM_GOTO_LEVEL;
        message.arg1 = mLevel;
        mCallback.sendMessage(message);
    }

    /**
     * 游戏结束。
     * @param allOver   全部通关则为 true，否则为 false
     */
    private void gameOver(boolean allOver) {
        // 将状态设置为已结束
        mStatus = Status.END;

        // 发送通知
        final Message message = Message.obtain();
        message.what = Const.UM_GAME_OVER;
        message.obj = allOver;
        mCallback.sendMessage(message);
    }

    /**
     * 获取当前关卡数。
     * @return 当前关卡数
     */
    public int getLevel() {
        return mLevel;
    }

    /**
     * 进入指定的关卡。
     * @param level 关卡数
     */
    public void gotoLevel(int level) {
        if (level <= Const.LEVEL_COUNT) {
            mLevel = level;
            this.enterLevel();
        }
    }

    /**
     * 进入下一关。
     */
    public void gotoNextLevel() {
        if (mLevel < Const.LEVEL_COUNT) {
            mLevel++;
            this.enterLevel();
        }
    }

    /**
     * 判断是否胜利。
     * @return 胜利则返回 true，否则返回 false
     */
    private boolean isWon() {
        return mBoxCount == 0;
    }

    /**
     * 滑动手势的响应方法。
     * @param direction 方向
     */
    public void onFling(int direction) {
        // 如果没在玩，直接返回
        if (mStatus != Status.PLAYING) {
            return;
        }

        // 移动小人。如果不能移动，则直接返回
        final BaseGameObject.MoveResult moveResult = new BaseGameObject.MoveResult();
        final BaseGameObject movedBox = mMan.move(direction, moveResult);
        if (moveResult.result == BaseGameObject.MoveResult.Result.NO_MOVE) {
            return;
        }

        // 重绘游戏区域
        mGameBoard.invalidate();

        // 增加移动次数
        mMoveCount++;

        // 发送通知
        final Message message = Message.obtain();
        message.what = Const.UM_MAM_MOVED;
        message.arg1 = mMoveCount;
        mCallback.sendMessage(message);

        // 判断是否胜利
        if (moveResult.result == BaseGameObject.MoveResult.Result.ARRIVED_ON_DEST) {
            mBoxCount--;
            if (this.isWon()) {
                final boolean allOver = mLevel == Const.LEVEL_COUNT;
                this.gameOver(allOver);
                return;
            }
        } else if (moveResult.result == BaseGameObject.MoveResult.Result.LEFT_FROM_DEST) {
            mBoxCount++;
        }

        // 加本次移动加入列表
        mMoveList.addLast(new MoveNode(movedBox, direction));
    }

    /**
     * 重玩本关。
     */
    public void restart() {
        this.enterLevel();
    }

    /**
     * 回退，即撤消上一次操作。
     */
    public void undo() {
        if (mMoveCount == 0) {
            return;
        }

        // 计算方向（方向与最后一次移动的方向相反）
        final MoveNode moveListNode = mMoveList.removeLast();
        int direction = moveListNode.getDirection();
        if (direction == R.id.IDC_BUTTON_LEFT) {
            direction = R.id.IDC_BUTTON_RIGHT;
        } else if (direction == R.id.IDC_BUTTON_RIGHT) {
            direction = R.id.IDC_BUTTON_LEFT;
        } else if (direction == R.id.IDC_BUTTON_UP) {
            direction = R.id.IDC_BUTTON_DOWN;
        } else if (direction == R.id.IDC_BUTTON_DOWN) {
            direction = R.id.IDC_BUTTON_UP;
        }

        // 移动小人
        final BaseGameObject.MoveResult moveResult = new BaseGameObject.MoveResult();
        mMan.move(direction, moveResult);

        // 移动箱子
        final BaseGameObject box = moveListNode.getBox();
        if (box != null) {
            box.move(direction, moveResult);
            if (moveResult.result == BaseGameObject.MoveResult.Result.LEFT_FROM_DEST) {
                mBoxCount++;
            } else if (moveResult.result == BaseGameObject.MoveResult.Result.ARRIVED_ON_DEST) {
                mBoxCount--;
            }
        }

        // 发送通知
        mMoveCount--;
        final Message message = Message.obtain();
        message.what = Const.UM_MAM_MOVED;
        message.arg1 = mMoveCount;
        mCallback.sendMessage(message);
    }
}
